package require TclOO

oo::class create Charmm {
    constructor { {conf {}}} {
	    my variable configuration
        my autosetup
	    array set configuration $conf
    }
    destructor {
         my variable interface
         if [info exists interface] {
                $interface destroy 
          }
    }
}


# core utilities
oo::define Charmm method start { {config {}} } {
    my variable interface
    my variable configuration
    set interface [Interface new]
    # append new configuration
    array set configuration $config
    #prepare environment 
    if ![info exists configuration(bin)] { error "Binary file haven't been setup for interfacing'"} elseif { ![file exists $configuration(bin)]} {
	error "Binary file does not exist in the path specified!"
    } else {
	set bin $configuration(bin) 
    }
    if {[info exists configuration(compressOutput)]} {
       if {[string compare $configuration(compressOutput) yes] == 0 } {$interface compressOutput}
    }  
    if {![info exists configuration(dir)] || [string match current* $configuration(dir) ] ==1 } { 
        set configuration(dir) $configuration(base)
        $interface setDir $configuration(dir)
    } else {
        if {[string compare [file pathtype $configuration(dir)] relative] ==0} {set configuration(dir) [file join $configuration(base) $configuration(dir)]}
        $interface setDir $configuration(dir)
    }
    if {![info exists configuration(cmdlog)] || [string compare $configuration(cmdlog) no] ==0 } {
        $interface log input stdout
        $interface loglevel input 0 1
    } else {
	$interface log input $configuration(cmdlog)
	$interface loglevel input 10 0
    }
    if {![info exists configuration(outputlog)] } {
        $interface log output stdout
        $interface loglevel output 10 0
    } elseif { [string compare $configuration(outputlog) no] == 0 } {
        $interface log output stdout
        $interface loglevel output 0 1
    } else {
        $interface log output $configuration(outputlog)
        $interface loglevel output 10 0
    }
    
    #start charmm
    $interface openChannel $bin
    $interface send "*start \n\n UNBUFIO\n"
    $interface expect {
	UNBUFIO { my print 1 "Charmm started" }
    }
}

oo::define Charmm method sendCmd { cmd } {
    my variable interface
    my variable configuration
    $interface send "$cmd\nlabel TCLBLOCKFINISHED\n"
    set output ""
    $interface expect {
	TCLBLOCKFINISHED {
	    append output $buffer
	    #puts "buffer: $buffer"
	}
	TERMINATING {
	    # set errormsg [getErr $buffer]
	    error "[self]: Error in charmm,Quiting..."
	    my destroy
	}
	TERMINATION {
	    my destroy
	}
    }
    return $output
}

oo::define Charmm method do {cmds {until done } { condition {}}} {
    my variable ener
    set out ""
    set methods [info class methods [self class]]
    set variables [info object vars [self] ]
#    puts $methods
    foreach m $methods {
#       # expose my method to method
	set cmds [regsub -all -lineanchor -- "^\\s*$m\\s" $cmds " [self] $m "]
	set cmds [regsub -all -- "\\\[\\s*$m\\s" $cmds "\[ [self] $m "]
    }
    #puts $cmds
#    #puts $variables
    foreach v $variables {
	my variable $v
    }
#  
    switch $until {
	until {
	    append out [ my eval $cmds]
	    while { ![my eval "expr $condition" ]} {
                uplevel 1 "array set ener \{[array get ener]\}" 
		puts "[self] $condition failed: [uplevel 1 subst  \{$condition\}] ."
		append out [ my eval $cmds]
	    }
            uplevel 1 "array set ener \{[array get ener]\}" 
	    puts "[self] $condition satisfied: [uplevel 1 subst -nocommands \{$condition\}] ."
	    
	}
	for {
	    for {set i 0 } { $i < $condition} { incr i} {
		append out [ uplevel 1 $cmds]
	    }
	}
	default {
	    append out [my eval $cmds]
	}
    }
    #puts "[self] finished"
    return $out
}

oo::define Charmm method abort {} {
     my variable interface
     my sendCmd "stop"
     if [info exists interface] {
       catch {
        $interface destroy 
       }
     }
}


# setup commands
oo::define Charmm method setup { config } {
    my variable configuration
    array set configuration $config
}

# helper functions

oo::define Charmm method autosetup {} {
   my variable configuration
   set configuration(base) [pwd]
   set configuration(dir) [lindex [split [self ] ::] end]
   set configuration(cmdlog) no
   set configuration(outputlog) [lindex [split [self ] ::] end]
   #check charmm binary file 
   if [ catch { set Bin_imm1pb [exec which imm1pb] }] { set Bin_imm1pb {} }
   if [ catch { set Bin_charmm [exec which charmm] }] { set Bin_charmm {} }
   if {[ catch { set configuration(bin)  [lindex [concat $Bin_imm1pb $Bin_charmm] 0] }] } { set configuration(bin) "" }
   if {[string length $configuration(bin)] <=0 } { puts "No binary found in PATH. Please specify by :\n setup \{bin \$path \}"}
}
oo::define Charmm method printConfig {} {
    my variable configuration
    parray configuration
}

